package jnpf.controller;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.core.text.StrPool;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.gson.reflect.TypeToken;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.Operation;
import jnpf.base.service.SysconfigService;
import jnpf.config.JnpfOauthConfig;
import jnpf.consts.DeviceType;
import jnpf.consts.LoginTicketStatus;
import jnpf.granter.TokenGranterBuilder;
import jnpf.model.LoginConfigModel;
import jnpf.model.LoginModel;
import jnpf.permission.controller.SocialsUserController;
import jnpf.permission.model.socails.SocialsUserVo;
import jnpf.permission.service.UserService;
import jnpf.service.AuthService;
import jnpf.base.ActionResult;
import jnpf.util.NoDataSourceBind;
import jnpf.base.UserInfo;
import jnpf.config.ConfigValueUtil;
import jnpf.model.LoginTicketModel;
import jnpf.model.BaseSystemInfo;
import jnpf.model.login.PcUserVO;
import jnpf.permission.entity.UserEntity;
import jnpf.exception.LoginException;
import jnpf.model.LoginForm;
import jnpf.model.LoginVO;
import jnpf.service.LoginService;
import jnpf.util.*;
import jnpf.util.data.DataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import util.util.DateUtil;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import util.util.Md5;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URLEncoder;
import java.security.Principal;
import java.util.*;
import static jnpf.consts.AuthConsts.PARAMS_JNPF_TICKET;
import static util.SimpleExample.setSms;

/**
 * 登录控制器
 *
 * @author JNPF开发平台组
 * @version V3.1.0
 * @copyright 引迈信息技术有限公司
 * @date 2019年9月26日 上午9:18
 */
@Tag(name = "登陆数据", description = "oauth")
@Slf4j
@RestController
@RequestMapping("/api/oauth")
public class LoginController {

    @Autowired
    private UserService userApi;
    @Autowired
    private LoginService loginService;
    @Autowired
    private AuthService authService;
    @Autowired
    private ConfigValueUtil configValueUtil;
    @Autowired
    private JnpfOauthConfig oauthConfig;
    @Autowired
    private SysconfigService sysConfigApi;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private TokenGranterBuilder tokenGranterBuilder;
    @Autowired
    private SocialsUserController socialsUserApi;

    @Value("${system.msgXlh}")
    private String msgXlh;
    @Value("${system.msgMy}")
    private String msgMy;
    @Value("${system.msgFwh}")
    private String msgFwh;
    @Value("${system.msgUrl}")
    private String msgUrl;
    /**
     * 登陆
     *
     * @param parameters 登录参数
     * @return
     * @throws LoginException 登录异常
     */
    @Operation(summary = "登陆")
    @Parameters({
            @Parameter(name = "parameters", description = "登录参数", required = true)
    })
    @RequestMapping(value = "/Login/**", method = {RequestMethod.GET, RequestMethod.POST})
    public ActionResult<LoginVO> login(@RequestParam Map<String, String> parameters) throws LoginException {
        return authService.login(parameters);
    }


    @Parameters({
            @Parameter(name = "account", description = "账号", required = true),
            @Parameter(name = "password", description = "密码", required = true)
    })
    @Operation(summary = "App登陆")
    @PostMapping(value = "/appLogin")
    public ActionResult appLogin(@RequestBody LoginForm loginForm) throws LoginException {
        UserInfo userInfo = new UserInfo();
        QueryWrapper<UserEntity> qw = new QueryWrapper<>();
        qw.lambda().eq(UserEntity::getAccount,loginForm.getAccount().trim());
        String pw = Md5Util.getStringMd5(loginForm.getPassword());
        UserEntity ue = userApi.getOne(qw);
        Map<String, String> parameters = new HashMap<>();
        long time = System.currentTimeMillis()/1000;
        parameters.put("n",time+"");
        parameters.put("account",loginForm.getAccount());
        parameters.put("password",pw);
        parameters.put("jnpf_ticket","");
        parameters.put("origin","password");
        parameters.put("code","");
        parameters.put("client_id",loginForm.getAccount());
        parameters.put("client_secret",loginForm.getPassword());
        parameters.put("scope","all");
        parameters.put("grant_type","password");
        if(null!=ue){
            //校验密码
            pw = Md5Util.getStringMd5(pw.toLowerCase() + ue.getSecretkey().toLowerCase());
            if(pw.equals(ue.getPassword())) {
                userInfo =JsonUtil.getJsonToBean(ue, UserInfo.class);
                if(StringUtil.isEmpty(userInfo.getUserId())){
                    userInfo.setUserId(userInfo.getId());
                }



//                UserProvider.setLoginUser(userInfo);

                return ActionResult.success(userInfo);
            } else {
                return ActionResult.fail("密码错误！");
            }
        }
        authService.login(parameters);
//        UserProvider.setLoginUser(UserProvider.getUser());
//        return ActionResult.success(UserProvider.getUser());
        return ActionResult.fail("账号不存在！");
//        return ActionResult.success();
    }



    /**
     * 验证密码
     *
     * @param loginForm 登录模型
     * @return
     * @throws LoginException 登录异常
     */
    @Operation(summary = "锁屏解锁登录")
    @Parameters({
            @Parameter(name = "loginForm", description = "登录模型", required = true)
    })
    @PostMapping("/LockScreen")
    public ActionResult lockScreen(@RequestBody LoginForm loginForm) throws LoginException {
        UserEntity userEntity = userApi.getUserByAccount(loginForm.getAccount());
        if (userEntity == null) {
            UserInfo userInfo = UserProvider.getUser();
            if(userInfo.getUserId() != null){
                UserProvider.logoutByUserId(userInfo.getUserId());
            }
            throw new LoginException("账号不存在");
        }
        if (!Md5Util.getStringMd5(loginForm.getPassword().toLowerCase() + userEntity.getSecretkey().toLowerCase()).equals(userEntity.getPassword())) {
            throw new LoginException("账户或密码错误，请重新输入。");
        }
        return ActionResult.success("验证成功");
    }

    /**
     * 登录注销
     *
     * @param grandtype 登录类型
     * @return
     */
    @NoDataSourceBind
    @Operation(summary = "退出")
    @Parameters({
            @Parameter(name = "grandtype", description = "登录类型", required = true)
    })
    @RequestMapping(value = {"/Logout","/Logout/{grandtype}" }, method = {RequestMethod.GET, RequestMethod.POST})
    public ActionResult logout(@PathVariable(value = "grandtype", required = false) String grandtype) {
        return tokenGranterBuilder.getGranterByLogin(grandtype).logout();
    }

    /**
     * 踢出指定用户, 推送Websocket用户被强制下线
     *
     * @param tokens token集合
     * @param userId 租户id
     * @param tenantId 租户id
     */
    @NoDataSourceBind
    @Operation(summary = "踢出指定用户")
    @Parameters({
            @Parameter(name = "tokens", description = "token集合"),
            @Parameter(name = "userId", description = "租户id"),
            @Parameter(name = "tenantId", description = "租户id"),
    })
    @PostMapping(value = {"/KickoutToken" })
    public void kickoutByToken(@RequestParam(value = "tokens", required = false) String[] tokens, @RequestParam(name = "userId", required = false) String userId, @RequestParam(name = "tenantId", required = false) String tenantId) {
        if(StringUtil.isNotEmpty(tokens)) {
            authService.kickoutByToken(tokens);
        }else{
            authService.kickoutByUserId(userId, tenantId);
        }
    }

    /**
     * 获取用户登录信息
     *
     * @param type Web/App
     * @return
     * @throws LoginException 登录异常
     */
    @Operation(summary = "获取用户登录信息")
    @Parameters({
            @Parameter(name = "type", description = "Web/App")
    })
    @GetMapping("/CurrentUser")
    public ActionResult<PcUserVO> currentUser(String type) throws LoginException {
        if (StringUtil.isEmpty(type)) {
            type = "Web";
        } else {
            type = "App";
        }
        UserInfo userInfo = UserProvider.getUser();
        if(DeviceType.TEMPUSERLIMITED.getDevice().equals(userInfo.getLoginDevice())){
            throw new LoginException("限制会话, 不允许访问系统");
        }
        PcUserVO pcUserVO = loginService.getCurrentUser(type);
        if (pcUserVO == null) {
            throw new LoginException("账户异常");
        }
        return ActionResult.success(pcUserVO);
    }

    /**
     * 修改密码信息发送
     *
     */
    @Operation(summary = "修改密码信息发送")
    @PostMapping("/updatePasswordMessage")
    public ActionResult updatePasswordMessage() {
        loginService.updatePasswordMessage();
        return ActionResult.success();
    }

    /**
     * 图形验证码
     *
     * @param codeLength 验证码长度
     * @param timestamp 验证码标识
     */
    @NoDataSourceBind()
    @Operation(summary = "图形验证码")
    @Parameters({
            @Parameter(name = "codeLength", description = "验证码长度", required = true),
            @Parameter(name = "timestamp", description = "验证码标识", required = true)
    })
    @GetMapping("/ImageCode/{codeLength}/{timestamp}")
    public void imageCode(@PathVariable("codeLength") Integer codeLength, @PathVariable("timestamp") String timestamp) {
        DownUtil.downCode(codeLength);
        redisUtil.insert(timestamp, ServletUtil.getSession().getAttribute(CodeUtil.RANDOMCODEKEY), 300);
    }

    /**
     * 注销用户
     *
     * @return
     */
    @Operation(summary = "注销用户")
    @PostMapping("/logoutCurrentUser")
    public ActionResult logoutCurrentUser() {
        UserInfo userInfo = UserProvider.getUser();
        if (userInfo.getIsAdministrator() != null && UserProvider.getUser().getIsAdministrator()) {
            return ActionResult.fail("管理员不能注销");
        }
        if (userInfo.getIsAdministrator() != null) {
            if (!userInfo.getIsAdministrator()) {
                userApi.delete(userApi.getInfo(userInfo.getUserId()));
                UserProvider.kickoutByUserId(userInfo.getUserId(), DataSourceContextHolder.getDatasourceId());
            }
        }
        return ActionResult.success("注销成功");
    }

    /**
     * 判断是否需要验证码
     *
     * @param account 账号
     */
    @NoDataSourceBind()
    @Operation(summary = "判断是否需要验证码")
    @Parameters({
            @Parameter(name = "account", description = "账号", required = true)
    })
    @GetMapping("/getConfig/{account}")
    public ActionResult<LoginModel> check(@PathVariable("account") String account) throws LoginException {
        LoginModel loginModel = new LoginModel();
        String tenantId = "0";
        String tenantDbConnectionString = "0";
        if (configValueUtil.isMultiTenancy()) {
            LoginForm loginForm = new LoginForm();
            loginForm.setAccount(account);
            UserInfo userInfo = new UserInfo();
            userInfo.setUserAccount(loginForm.getAccount());
            userInfo = loginService.getTenantAccount(userInfo);
            tenantId = userInfo.getTenantId();
            tenantDbConnectionString = userInfo.getTenantDbConnectionString();
        }
        // 获取配置
        BaseSystemInfo sysConfigInfo = sysConfigApi.getSysInfo();
        // 是否开启验证码
        if (Objects.nonNull(sysConfigInfo) && "1".equals(String.valueOf(sysConfigInfo.getEnableVerificationCode()))) {
            loginModel.setEnableVerificationCode(1);
            Integer verificationCodeNumber = sysConfigInfo.getVerificationCodeNumber();
            loginModel.setVerificationCodeNumber(verificationCodeNumber == null ? 4 : verificationCodeNumber);
            return ActionResult.success(loginModel);
        }
        loginModel.setEnableVerificationCode(0);
        return ActionResult.success(loginModel);
    }

    /**
     * 获取登录配置, 是否需要跳转、第三方登录信息
     *
     * @return {re}
     * @throws LoginException 登录异常
     */
    @NoDataSourceBind()
    @Operation(summary = "获取登录配置")
    @GetMapping("/getLoginConfig")
    public ActionResult<LoginConfigModel> getLoginConfig() {
        LoginConfigModel loginConfigModel = new LoginConfigModel();
        if(oauthConfig.getSsoEnabled()){
            String url = oauthConfig.getLoginPath() + StrPool.SLASH + oauthConfig.getDefaultSSO();
            loginConfigModel.setRedirect(true);
            loginConfigModel.setUrl(url);
            loginConfigModel.setTicketParams(PARAMS_JNPF_TICKET);
        } else {
            //追加第三方登录配置
            List<SocialsUserVo> loginList = socialsUserApi.getLoginList(PARAMS_JNPF_TICKET.toUpperCase());
            if (CollectionUtil.isNotEmpty(loginList)) {
                loginConfigModel.setSocialsList(loginList);
                loginConfigModel.setRedirect(false);
                loginConfigModel.setTicketParams(PARAMS_JNPF_TICKET);
            }
        }
        return ActionResult.success(loginConfigModel);
    }


    /**
     * 获取登录票据
     * @return {msg:有效期, data:票据}
     */
    @NoDataSourceBind()
    @Operation(summary = "获取登录票据")
    @GetMapping("/getTicket")
    public ActionResult<String> getTicket() {
        LoginTicketModel ticketModel = new LoginTicketModel();
        ticketModel.setTicketTimeout(System.currentTimeMillis() + oauthConfig.getTicketTimeout()*1000);
        String ticket = TicketUtil.createTicket(ticketModel, oauthConfig.getTicketTimeout());
        return ActionResult.success(ticketModel.getTicketTimeout().toString(), ticket);
    }

    /**
     * 检测票据登录状态
     * @return {re}
     * @throws LoginException
     */
    @NoDataSourceBind()
    @Operation(summary = "获取登录状态")
    @GetMapping("/getTicketStatus/{ticket}")
    public ActionResult<LoginTicketModel> getTicketStatus(@PathVariable("ticket") String ticket) {
        LoginTicketModel ticketModel = TicketUtil.parseTicket(ticket);
        if(ticketModel == null){
            ticketModel = new LoginTicketModel().setStatus(LoginTicketStatus.Invalid.getStatus()).setValue("票据失效！");
        }else {
            if (ticketModel.getStatus() != LoginTicketStatus.UnLogin.getStatus()&&ticketModel.getStatus() != LoginTicketStatus.UnBind.getStatus()) {
                TicketUtil.deleteTicket(ticket);
            }
        }
        return ActionResult.success(ticketModel);
    }



    public String getMsgCode(String mobile){
        String appId = "3SDK-TJX-0130-MGTMT";// 请联系销售，或者在页面中 获取
        // 密钥
        String secretKey = "882D0DA6D00F346B";// 请联系销售，或者在页面中 获取
        // 接口地址
        String host = "http://bjmtn.b2m.cn:80";// 请联系销售获取
//        String host = "http://bjmtn.b2m.cn:80/simpleinter/sendSMS";
        Random random = new Random();
        String msgcode="";
        for (int i=0;i<6;i++)
        {
            msgcode+=random.nextInt(10);
        }
        // 时间戳
        String timestamp = DateUtil.toString(new Date(), "yyyyMMddHHmmss");
        // 签名
        String sign = Md5.md5((appId + secretKey + timestamp).getBytes());
        String cont = "【津药达仁堂】您的动态验证码是:"+msgcode;
        setSms(appId, sign, timestamp, host, cont, mobile, "030621", null, "");// 如果通道已绑定签名，可以去掉【某某公司】
        return msgcode;
    }


    @Operation(summary = "获取手机验证码")
    @GetMapping("/getMsgBymobile/{mobile}")
    public ActionResult getMsgBymobile(@PathVariable("mobile") String mobile) {
//        String code = getMessageCode(mobile);
        String code = getMsgCode(mobile);
        redisUtil.insert(mobile,code,100L);
//        return redisUtil.getString(mobilenum).toString();
        return ActionResult.success(code);
    }

    /**
     *
     * @param mobile
     * @param code
     * @return
     */
    @Operation(summary = "手机登录")
    @GetMapping("/loginCheck/{mobile}/{code}")
    public ActionResult loginCheck(@PathVariable("mobile") String mobile,@PathVariable("code") String code) {
        String mm = redisUtil.getString(mobile)+"";
        if(!StringUtil.isNotEmpty(mm)){
            return ActionResult.fail("验证码过期！");
        }
        QueryWrapper<UserEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(UserEntity::getAccount,mobile.trim());
        List<UserEntity> list = this.userApi.list(queryWrapper);
        if(list.size()>0){
            if(code.equals(mm)) {
                return ActionResult.success(list.get(0));
            } else {
                return ActionResult.fail("验证码错误！");
            }
        } else {
            return ActionResult.fail("用户不存在！");
        }

    }

}
